
package interprete;

import interprete.DataCell.Ttipo;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * Procesadores de Lenguaje, curso 2009-2010
 * @author grupo 02
 *
 */

class MemoryManager{



    private void decreaseHeap(int size) {
        //como hemos empequeñecido el tamaño del heap debemos ajustar la lista de
        //posiciones "Libres"
        Iterator it = libre.iterator();
        while(!it.hasNext()){
            FreePage fp=(FreePage)it.next();
            //eliminamos todas las paginas ahora pertenecientes al area estatica
            if(fp.address < size){
                it.remove();
            }
            //redimensionamos el hueco que representaba parte del ahora "area estatica" de memoria
            else if(fp.address - fp.size < (size-1)){
                fp.size = fp.address-(size-1);
            }
        }
        
    }

    private void increaseHeap(int size){
        //añadimos un hueco con el nuevo area.
        //direccion : this.tam_memory-this.tam_heap.
        //tamaño : espacio de crecimiento del heap.
        FreePage fp= new FreePage(this.tam_memory-this.tam_heap-1,this.tam_memory-this.tam_heap-1-(size-1));
        libre.add(fp); 

    }

    private class FreePage{
        public int address;
        public int size;

        public FreePage(int add, int size){
            this.address = add;
            this.size = size;
            }
        }



        protected  DataCell[] memory;
        protected int tam_estatico;
        protected int tam_heap;
        private int tam_memory;
        private LinkedList libre;


        MemoryManager(int memory_size,int tam_estatico){

            this.memory = new DataCell[memory_size];
            this.tam_estatico = tam_estatico;
            this.tam_heap = memory_size-tam_estatico;
            this.tam_memory=memory_size;
            this.libre = new LinkedList();
            //insertamos en la lista de memoria libre toda la memoria.
            libre.add(new FreePage(tam_memory-1,tam_heap));


        }

        public String toString(){

            String result="[";

            for (int i=0;i<tam_memory;i++)
                if(memory[i]!=null)
                    result+="{"+i+" = "+memory[i].data+" }";
            result+="]";
            return result;

        }

        public DataCell get(int pos){
            return memory[pos];
        }
        public void put(int pos, DataCell data){
            memory[pos]=data;

        }

        public int allocate(int size) throws HeapException{
       
            //Buscamos un hueco:
            Iterator it;
            FreePage fp;
            int direccion;
            it = libre.iterator();
            while (it.hasNext()){
                fp=(FreePage) it.next();
                if (size<fp.size){
                    //hueco suficiente.
                    direccion = fp.address;
                    fp.address-=size;
                    fp.size-=size;
                    return direccion;
                }
                if (size == fp.size){
                    direccion = fp.address;
                    //borramos de la lista de huecos libres
                    it.remove();
                    return direccion;

                }

            }
            //si hemos llegado aqui no hay espacio suficiente:
            throw new HeapException("no hay suficiente espacio en el HEAP");

       }

        public void deallocate(int size, int address) throws MemoryAccessException{
            //comprobamos que la direccion es valida:
            if( address >= this.tam_memory)
                throw new MemoryAccessException("Acceso a posicion de memoria no disponible ");

            else if (address < (this.tam_memory - this.tam_heap))
                throw new MemoryAccessException("Acceso fuera del area de heap");

            //parametros adecuados:
            FreePage fp=new FreePage(address, size);
            libre.add(fp);

            //poner a null las direcciones de memoria liberadas (para evitar que las
            //escriba cuando el "toSrtring()"
            for(int i=0;i<size;i++)
                memory[address-i]=null;
        }

        public void resize(int size) throws MemoryAccessException{
            //comprobamos tamaño
            if (size <= 0 ||
                size > this.tam_memory )
                throw new MemoryAccessException("redimensionamiento de memoria no permitido, tamaño no permitido");

            //inicializamos area de memoria estatica (excepto CP y 1º Display)
            for(int i=2;i<size;i++)
                this.memory[i] = new DataCell(0, Ttipo.INT);

            //modificamos el tamaño de memoria asignada al area estatica:
            if(size > this.tam_estatico)
                //decrecimiento de heap
                decreaseHeap(size);
            else
                //crecimiento de heap
                increaseHeap(size);
            
            this.tam_estatico = size;
            this.tam_heap = tam_memory-tam_estatico;

        }



    }
